#pragma once

#include "NanoflannKdTree.h"
#include <vector>

template <int N>
class MeanShift
{
public:
	typedef Vector<double, N>			Point;
	typedef NanoflannKdTree<double, N>	KDTree;
	typedef Vector<int, N>				Grid_Coord;

	std::vector<Point>	data_points;		// the original data
	KDTree*				kd_tree;

	double				grid_stride;			// the stride used to create grids
	Grid_Coord			grid_radix;			// the grid size along each dimension
	std::vector<Point>	grid_samples;		// resample the data with a regular grid

	std::vector<std::vector<int>>	candidate_tags;		// the candidate mode ids for each point
	std::vector<Point>				candidate_modes;	// the candidate peeks
	std::vector<double>				candidate_density;	// the density at candidate modes
	std::vector<int>				candidate_to_mode;	// map from candidate index to mode index
	std::vector<Point>				modes;				// the final modes after fusing
	std::vector<std::vector<int>>	clusters;			// corresponding to modes

	Point	bbmin, bbmax;	// AABB
	double	h;				// bandwidth
	double	e;				// convergence threshold
	double	rho;			// density threshold for being a mode

public:
	// Constructor
    MeanShift(std::vector< Point > &pts);
	void	setParameters();

	// regular grid within the AABB
	void	createGrid();
	bool	moveToNext(Grid_Coord &gcoord);
	Point	gridPoint(Grid_Coord gcoord);

	// clustering
	void	seekCandidateModes();
	void	mergeCandidateModes();
	void	cluster();

	// access to results
	std::vector<Point>	pointsInCluster(int cid);
	QSet<int>	pointIDsNearMode(int cid, int scale = 1);


	// helper
	int		dim(){return Point::dim();}					// dim
	void	computeAABB();								// AABB
	double	kernelEpanechnikov(double u);				// kernel
	Point	meanOf(KDResults &kd_results);				// the new mean at point p
	double	densityAt(Point p);							// the density
	int		majorityOf(std::vector<int> &item_array);	// majority element in an array
};


// To do: include all the implementation from a text file
